/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- *//* vim:set ts=4 sw=4 sts=4 et cin: *//* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */// HttpLog.h should generally be included first#include"HttpLog.h"#include"nsHttp.h"#include"PLDHashTable.h"#include"mozilla/Mutex.h"#include"mozilla/HashFunctions.h"#include"nsCRT.h"#include<errno.h>namespacemozilla{namespacenet{// define storage for all atoms#define HTTP_ATOM(_name, _value) nsHttpAtom nsHttp::_name = { _value };#include"nsHttpAtomList.h"#undef HTTP_ATOM// find out how many atoms we have#define HTTP_ATOM(_name, _value) Unused_ ## _name,enum{#include"nsHttpAtomList.h"NUM_HTTP_ATOMS};#undef HTTP_ATOM// we keep a linked list of atoms allocated on the heap for easy clean up when// the atom table is destroyed. The structure and value string are allocated// as one contiguous block.structHttpHeapAtom{structHttpHeapAtom*next;charvalue[1];};staticPLDHashTable*sAtomTable;staticstructHttpHeapAtom*sHeapAtoms=nullptr;staticMutex*sLock=nullptr;HttpHeapAtom*NewHeapAtom(constchar*value){intlen=strlen(value);HttpHeapAtom*a=reinterpret_cast<HttpHeapAtom*>(malloc(sizeof(*a)+len));if(!a)returnnullptr;memcpy(a->value,value,len+1);// add this heap atom to the list of all heap atomsa->next=sHeapAtoms;sHeapAtoms=a;returna;}// Hash string ignore case, based on PL_HashStringstaticPLDHashNumberStringHash(constvoid*key){PLDHashNumberh=0;for(constchar*s=reinterpret_cast<constchar*>(key);*s;++s)h=AddToHash(h,nsCRT::ToLower(*s));returnh;}staticboolStringCompare(constPLDHashEntryHdr*entry,constvoid*testKey){constvoid*entryKey=reinterpret_cast<constPLDHashEntryStub*>(entry)->key;returnPL_strcasecmp(reinterpret_cast<constchar*>(entryKey),reinterpret_cast<constchar*>(testKey))==0;}staticconstPLDHashTableOpsops={StringHash,StringCompare,PLDHashTable::MoveEntryStub,PLDHashTable::ClearEntryStub,nullptr};// We put the atoms in a hash table for speedy lookup.. see ResolveAtom.nsresultnsHttp::CreateAtomTable(){MOZ_ASSERT(!sAtomTable,"atom table already initialized");if(!sLock){sLock=newMutex("nsHttp.sLock");}// The initial length for this table is a value greater than the number of// known atoms (NUM_HTTP_ATOMS) because we expect to encounter a few random// headers right off the bat.sAtomTable=newPLDHashTable(&ops,sizeof(PLDHashEntryStub),NUM_HTTP_ATOMS+10);// fill the table with our known atomsconstchar*constatoms[]={#define HTTP_ATOM(_name, _value) nsHttp::_name._val,#include"nsHttpAtomList.h"#undef HTTP_ATOMnullptr};for(inti=0;atoms[i];++i){autostub=static_cast<PLDHashEntryStub*>(sAtomTable->Add(atoms[i],fallible));if(!stub)returnNS_ERROR_OUT_OF_MEMORY;MOZ_ASSERT(!stub->key,"duplicate static atom");stub->key=atoms[i];}returnNS_OK;}voidnsHttp::DestroyAtomTable(){deletesAtomTable;sAtomTable=nullptr;while(sHeapAtoms){HttpHeapAtom*next=sHeapAtoms->next;free(sHeapAtoms);sHeapAtoms=next;}deletesLock;sLock=nullptr;}Mutex*nsHttp::GetLock(){returnsLock;}// this function may be called from multiple threadsnsHttpAtomnsHttp::ResolveAtom(constchar*str){nsHttpAtomatom={nullptr};if(!str||!sAtomTable)returnatom;MutexAutoLocklock(*sLock);autostub=static_cast<PLDHashEntryStub*>(sAtomTable->Add(str,fallible));if(!stub)returnatom;// out of memoryif(stub->key){atom._val=reinterpret_cast<constchar*>(stub->key);returnatom;}// if the atom could not be found in the atom table, then we'll go// and allocate a new atom on the heap.HttpHeapAtom*heapAtom=NewHeapAtom(str);if(!heapAtom)returnatom;// out of memorystub->key=atom._val=heapAtom->value;returnatom;}//// From section 2.2 of RFC 2616, a token is defined as://// token = 1*<any CHAR except CTLs or separators>// CHAR = <any US-ASCII character (octets 0 - 127)>// separators = "(" | ")" | "<" | ">" | "@"// | "," | ";" | ":" | "\" | <">// | "/" | "[" | "]" | "?" | "="// | "{" | "}" | SP | HT// CTL = <any US-ASCII control character// (octets 0 - 31) and DEL (127)>// SP = <US-ASCII SP, space (32)>// HT = <US-ASCII HT, horizontal-tab (9)>//staticconstcharkValidTokenMap[128]={0,0,0,0,0,0,0,0,// 00,0,0,0,0,0,0,0,// 80,0,0,0,0,0,0,0,// 160,0,0,0,0,0,0,0,// 240,1,0,1,1,1,1,1,// 320,0,1,1,0,1,1,0,// 401,1,1,1,1,1,1,1,// 481,1,0,0,0,0,0,0,// 560,1,1,1,1,1,1,1,// 641,1,1,1,1,1,1,1,// 721,1,1,1,1,1,1,1,// 801,1,1,0,0,0,1,1,// 881,1,1,1,1,1,1,1,// 961,1,1,1,1,1,1,1,// 1041,1,1,1,1,1,1,1,// 1121,1,1,0,1,0,1,0// 120};boolnsHttp::IsValidToken(constchar*start,constchar*end){if(start==end)returnfalse;for(;start!=end;++start){constunsignedcharidx=*start;if(idx>127||!kValidTokenMap[idx])returnfalse;}returntrue;}constchar*nsHttp::GetProtocolVersion(uint32_tpv){switch(pv){caseHTTP_VERSION_2:caseNS_HTTP_VERSION_2_0:return"h2";caseNS_HTTP_VERSION_1_0:return"http/1.0";caseNS_HTTP_VERSION_1_1:return"http/1.1";default:NS_WARNING(nsPrintfCString("Unkown protocol version: 0x%X. ""Please file a bug",pv).get());return"http/1.1";}}// staticvoidnsHttp::TrimHTTPWhitespace(constnsACString&aSource,nsACString&aDest){nsAutoCStringstr(aSource);// HTTP whitespace 0x09: '\t', 0x0A: '\n', 0x0D: '\r', 0x20: ' 'staticconstcharkHTTPWhitespace[]="\t\n\r ";str.Trim(kHTTPWhitespace);aDest.Assign(str);}// staticboolnsHttp::IsReasonableHeaderValue(constnsACString&s){// Header values MUST NOT contain line-breaks. RFC 2616 technically// permits CTL characters, including CR and LF, in header values provided// they are quoted. However, this can lead to problems if servers do not// interpret quoted strings properly. Disallowing CR and LF here seems// reasonable and keeps things simple. We also disallow a null byte.constnsACString::char_type*end=s.EndReading();for(constnsACString::char_type*i=s.BeginReading();i!=end;++i){if(*i=='\r'||*i=='\n'||*i=='\0'){returnfalse;}}returntrue;}constchar*nsHttp::FindToken(constchar*input,constchar*token,constchar*seps){if(!input)returnnullptr;intinputLen=strlen(input);inttokenLen=strlen(token);if(inputLen<tokenLen)returnnullptr;constchar*inputTop=input;constchar*inputEnd=input+inputLen-tokenLen;for(;input<=inputEnd;++input){if(PL_strncasecmp(input,token,tokenLen)==0){if(input>inputTop&&!strchr(seps,*(input-1)))continue;if(input<inputEnd&&!strchr(seps,*(input+tokenLen)))continue;returninput;}}returnnullptr;}boolnsHttp::ParseInt64(constchar*input,constchar**next,int64_t*r){MOZ_ASSERT(input);MOZ_ASSERT(r);char*end=nullptr;errno=0;// Clear errno to make sure its value is set by strtollint64_tvalue=strtoll(input,&end,/* base */10);// Fail if: - the parsed number overflows.// - the end points to the start of the input string.// - we parsed a negative value. Consumers don't expect that.if(errno!=0||end==input||value<0){LOG(("nsHttp::ParseInt64 value=%"PRId64" errno=%d",value,errno));returnfalse;}if(next){*next=end;}*r=value;returntrue;}boolnsHttp::IsPermanentRedirect(uint32_thttpStatus){returnhttpStatus==301||httpStatus==308;}template<typenameT>voidlocalEnsureBuffer(UniquePtr<T[]>&buf,uint32_tnewSize,uint32_tpreserve,uint32_t&objSize){if(objSize>=newSize)return;// Leave a little slop on the new allocation - add 2KB to// what we need and then round the result up to a 4KB (page)// boundary.objSize=(newSize+2048+4095)&~4095;static_assert(sizeof(T)==1,"sizeof(T) must be 1");autotmp=MakeUnique<T[]>(objSize);if(preserve){memcpy(tmp.get(),buf.get(),preserve);}buf=Move(tmp);}voidEnsureBuffer(UniquePtr<char[]>&buf,uint32_tnewSize,uint32_tpreserve,uint32_t&objSize){localEnsureBuffer<char>(buf,newSize,preserve,objSize);}voidEnsureBuffer(UniquePtr<uint8_t[]>&buf,uint32_tnewSize,uint32_tpreserve,uint32_t&objSize){localEnsureBuffer<uint8_t>(buf,newSize,preserve,objSize);}///voidParsedHeaderValueList::Tokenize(char*input,uint32_tinputLen,char**token,uint32_t*tokenLen,bool*foundEquals,char**next){if(foundEquals){*foundEquals=false;}if(next){*next=nullptr;}if(inputLen<1||!input||!token){return;}boolfoundFirst=false;boolinQuote=false;boolfoundToken=false;*token=input;*tokenLen=inputLen;for(uint32_tindex=0;!foundToken&&index<inputLen;++index){// strip leading cruftif(!foundFirst&&(input[index]==' '||input[index]=='"'||input[index]=='\t')){(*token)++;}else{foundFirst=true;}if(input[index]=='"'){inQuote=!inQuote;continue;}if(inQuote){continue;}if(input[index]=='='||input[index]==';'){*tokenLen=(input+index)-*token;if(next&&((index+1)<inputLen)){*next=input+index+1;}foundToken=true;if(foundEquals&&input[index]=='='){*foundEquals=true;}break;}}if(!foundToken){*tokenLen=(input+inputLen)-*token;}// strip trailing cruftfor(char*index=*token+*tokenLen-1;index>=*token;--index){if(*index!=' '&&*index!='\t'&&*index!='"'){break;}--(*tokenLen);if(*index=='"'){break;}}}ParsedHeaderValueList::ParsedHeaderValueList(char*t,uint32_tlen){char*name=nullptr;uint32_tnameLen=0;char*value=nullptr;uint32_tvalueLen=0;char*next=nullptr;boolfoundEquals;while(t){Tokenize(t,len,&name,&nameLen,&foundEquals,&next);if(next){len-=next-t;}t=next;if(foundEquals&&t){Tokenize(t,len,&value,&valueLen,nullptr,&next);if(next){len-=next-t;}t=next;}mValues.AppendElement(ParsedHeaderPair(name,nameLen,value,valueLen));value=name=nullptr;valueLen=nameLen=0;next=nullptr;}}ParsedHeaderValueListList::ParsedHeaderValueListList(constnsCString&fullHeader):mFull(fullHeader){char*t=mFull.BeginWriting();uint32_tlen=mFull.Length();char*last=t;boolinQuote=false;for(uint32_tindex=0;index<len;++index){if(t[index]=='"'){inQuote=!inQuote;continue;}if(inQuote){continue;}if(t[index]==','){mValues.AppendElement(ParsedHeaderValueList(last,(t+index)-last));last=t+index+1;}}if(!inQuote){mValues.AppendElement(ParsedHeaderValueList(last,(t+len)-last));}}}// namespace net}// namespace mozilla